package com.telecom.common.utils;

import com.telecom.common.convention.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * 雪花算法ID生成器
 * 
 * 雪花算法是一种分布式唯一ID生成算法，生成的ID为64位长整型数字
 * 结构如下：
 * 1位符号位(固定为0) + 41位时间戳 + 10位机器ID + 12位序列号
 * 
 * @author GWX.cz
 * @since 2025-01-16
 */
@Component
@Slf4j
public class SnowflakeIdGenerator {
    
    /**
     * 起始时间戳（2025-01-01 00:00:00 UTC的毫秒数）
     */
    private static final long START_TIMESTAMP = 1735689600000L;
    
    /**
     * 机器ID位数
     */
    private static final long MACHINE_ID_BITS = 10L;
    
    /**
     * 序列号位数
     */
    private static final long SEQUENCE_BITS = 12L;
    
    /**
     * 机器ID最大值
     */
    private static final long MAX_MACHINE_ID = ~(-1L << MACHINE_ID_BITS);
    
    /**
     * 序列号最大值
     */
    private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);
    
    /**
     * 机器ID左移位数
     */
    private static final long MACHINE_ID_SHIFT = SEQUENCE_BITS;
    
    /**
     * 时间戳左移位数
     */
    private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS;
    
    /**
     * 机器ID (0-1023)
     */
    private final long machineId;
    
    /**
     * 序列号
     */
    private long sequence = 0L;
    
    /**
     * 上次生成ID的时间戳
     */
    private long lastTimestamp = -1L;
    
    /**
     * 构造函数
     * 
     * @param machineId 机器ID (0-1023)
     */
    public SnowflakeIdGenerator(long machineId) {
        if (machineId > MAX_MACHINE_ID || machineId < 0) {
            throw new IllegalArgumentException(
                String.format("机器ID必须在0-%d之间", MAX_MACHINE_ID));
        }
        this.machineId = machineId;
        log.info("雪花算法ID生成器初始化完成，机器ID: {}", machineId);
    }
    
    /**
     * 默认构造函数，机器ID为0
     */
    public SnowflakeIdGenerator() {
        this(0L);
    }
    
    /**
     * 生成下一个ID
     * 
     * @return 唯一ID
     */
    public synchronized long nextId() {
        long currentTimestamp = getCurrentTimestamp();
        
        // 如果当前时间小于上次ID生成的时间戳，说明系统时钟回退过，抛出异常
        if (currentTimestamp < lastTimestamp) {
            throw new ServiceException(
                String.format("时钟回退异常，拒绝生成ID。当前时间戳: %d, 上次时间戳: %d", 
                    currentTimestamp, lastTimestamp));
        }
        
        // 如果是同一时间戳生成的，则序列号自增
        if (currentTimestamp == lastTimestamp) {
            sequence = (sequence + 1) & MAX_SEQUENCE;
            // 如果序列号溢出，则等待下一毫秒
            if (sequence == 0) {
                currentTimestamp = getNextTimestamp(lastTimestamp);
            }
        } else {
            // 如果是新的时间戳，则序列号重置为0
            sequence = 0L;
        }
        
        // 更新上次生成ID的时间戳
        lastTimestamp = currentTimestamp;
        
        // 生成ID：时间戳部分 | 机器ID部分 | 序列号部分
        return ((currentTimestamp - START_TIMESTAMP) << TIMESTAMP_SHIFT)
                | (machineId << MACHINE_ID_SHIFT)
                | sequence;
    }
    
    /**
     * 获取当前时间戳
     * 
     * @return 当前时间戳（毫秒）
     */
    private long getCurrentTimestamp() {
        return System.currentTimeMillis();
    }
    
    /**
     * 等待下一个毫秒
     * 
     * @param lastTimestamp 上次时间戳
     * @return 下一个毫秒的时间戳
     */
    private long getNextTimestamp(long lastTimestamp) {
        long timestamp = getCurrentTimestamp();
        while (timestamp <= lastTimestamp) {
            timestamp = getCurrentTimestamp();
        }
        return timestamp;
    }
    

}